#ifndef __MY_DATA__
#define __MY_DATA__
#include <unordered_map>
#include <pthread.h>
#include "fileUtil.hpp"
#include "config.hpp"

namespace cloudBackup
{
	// 已备份的文件的属性信息  backup file Attribute information
	typedef struct BackupAInfo // 都需要管理哪些信息
	{
		bool _isZipped;				  // 是否被压缩
		size_t _fsize;				  // 文件大小
		time_t _atime;				  // 最后一次访问
		time_t _ctime;				  // 最后一次状态改变
		time_t _mtime;				  // 最后一次修改
		std::string _backupPath;	  // 备份文件存放路径./backupDir/a.txt
		std::string _zipPath;		  // 压缩包存放路径 ./zipDir/a.txt.lz
		std::string _reqDownloadPath; // http_req 根据href超链接下载文件 ./download/a.txt

		BackupAInfo() {}
		BackupAInfo(const std::string &filePath)
		{
			NewBackupAInfo(filePath);
		}

		// BackupAInfo对象obj调用此函数 目的是设置filePath文件的属性信息
		// 传来的filePath一定是./backupDir/a.txt 因为只为备份文件添加信息
		// 而备份文件存放在./backupDir/a.txt
		bool NewBackupAInfo(const std::string &filePath)
		{
			// 无法为不存在的文件添加属性信息
			FileUtil fileUtil(filePath);
			if (fileUtil.Exists() == false)
			{
				Log::log(LOGERROR, "BackupInfo::NewBackupAInfo file not exists!: %s: %d", strerror(errno), errno);
				return false;
			}

			// 从配置文件读取相关信息
			Config *config = Config::GetInstance();
			std::string zipDir = config->GetZipDir();
			std::string zipSuffix = config->GetZipSuffix();
			std::string urlDownloadID = config->GetUrlDownloadID();

			this->_isZipped = false;
			this->_fsize = fileUtil.FileSize();
			this->_atime = fileUtil.LastATime();
			this->_ctime = fileUtil.LastCTime();
			this->_mtime = fileUtil.LastMTime();
			this->_backupPath = filePath;
			this->_zipPath = zipDir + fileUtil.FileName() + zipSuffix;	  // ./zipDir/a.txt.lz
			this->_reqDownloadPath = urlDownloadID + fileUtil.FileName(); // ./download/a.txt

			return true;
		}

		void showMsg()
		{
			std::cout << "_isZipped: " << _isZipped << std::endl;
			std::cout << "_fsize: " << _fsize << std::endl;
			std::cout << "_atime: " << _atime << std::endl;
			std::cout << "_ctime: " << _ctime << std::endl;
			std::cout << "_mtime: " << _mtime << std::endl;
			std::cout << "_backupPath: " << _backupPath << std::endl;
			std::cout << "_zipPath: " << _zipPath << std::endl;
			std::cout << "_reqDownloadPath: " << _reqDownloadPath << std::endl;
		}
	} BackupAInfo;

	// 数据管理类 怎么管理
	class DataManager
	{
	private:
		// 该文件存储诸多已备份文件的属性信息 json对象序列化成str保存在该文件
		std::string _backupAInfoFile; // backupAInfo.dat

		// 使用hash表在内存中管理数据 以reqDownloadPath作为key值--查询速度快
		// map[reqDownloadPath] = BackupAInfo;浏览器下载文件的时候总是以reqDownloadPath作为请求
		std::unordered_map<std::string, BackupAInfo> _table;

		// 读写锁：读共享 写互斥
		pthread_rwlock_t _rwlock;

	public:
		DataManager()
		{
			// 成员属性初始化
			_backupAInfoFile = Config::GetInstance()->GetBackupAInfoFName(); // 获取存放备份文件属性信息的文件
			pthread_rwlock_init(&_rwlock, NULL);							 // 初始化读写锁
			InitLoad();
		}

		~DataManager()
		{
			pthread_rwlock_destroy(&_rwlock); // 销毁读写锁
		}

		// 每次程序运行前 从 存放备份文件属性信息的文件中 中读取数据至内存中的哈希表
		// 服务端初次运行时 _backupAInfoFile文件不存在 此处创建
		bool InitLoad()
		{
			// 1. 将备份文件属性信息读取出来存至content binary->str
			FileUtil fileUtil(_backupAInfoFile);
			if (fileUtil.Exists() == false)
			{
				Log::log(INFO, "DataManager::InitLoad file not exist, now create new dat!");
				std::ofstream file(_backupAInfoFile);
				file.close();
				return true;
			}
			std::string content;
			fileUtil.GetContent(&content);

			// 2. str反序列化成json对象 str->json
			Json::Value root;
			JsonUtil::Deserialize(content, &root);

			// 3. 将反序列化得到的Json::Value中的数据添加到table中
			for (int i = 0; i < root.size(); i++)
			{
				// backupAInfo.dat中有某文件属性信息 但该文件已不存在
				// ZipPath/backupPath都不存在的文件没必要添加到table中
				FileUtil fileUtil(root[i]["_reqDownloadPath"].asString());

				Config *config = Config::GetInstance();
				std::string tmpZipPath = config->GetZipDir() + fileUtil.FileName() + config->GetZipSuffix();
				std::string tmpOriPath = config->GetBackupDir() + fileUtil.FileName();
				FileUtil fuZip(tmpZipPath);
				FileUtil fuOri(tmpOriPath);

				BackupAInfo info;
				if (fuOri.Exists() || fuZip.Exists())
				{
					info._isZipped = root[i]["_isZipped"].asBool();
					info._fsize = root[i]["_fsize"].asInt64();
					info._atime = root[i]["_atime"].asInt64();
					info._ctime = root[i]["_ctime"].asInt64();
					info._mtime = root[i]["_mtime"].asInt64();
					info._zipPath = root[i]["_zipPath"].asString();
					info._backupPath = root[i]["_backupPath"].asString();
					info._reqDownloadPath = root[i]["_reqDownloadPath"].asString();

					Insert_Table(info);
				}
			}

			Log::log(INFO, "DataManager::InitLoad read backupAInfoFile %s success!", _backupAInfoFile.c_str());
			return true;
		}

		// 将一个个 文件属性信息插入到表中
		bool Insert_Table(const BackupAInfo &info)
		{
			pthread_rwlock_wrlock(&_rwlock);
			_table[info._reqDownloadPath] = info;
			pthread_rwlock_unlock(&_rwlock);

			Storage();
			return true;
		}

		bool Update_Table(const BackupAInfo &info)
		{
			pthread_rwlock_wrlock(&_rwlock);
			_table[info._reqDownloadPath] = info;
			pthread_rwlock_unlock(&_rwlock);
			Storage();
			return true;
		}

		// 每次数据新增或修改后重新进行持久化存储 避免数据丢失
		bool Storage()
		{
			// 1. 获取当前表中的所有的BackupAInfo
			std::vector<BackupAInfo> array;
			this->GetAllBackupAInfo(&array);

			// 2. 添加到Json::Value
			Json::Value root;
			for (int i = 0; i < array.size(); i++)
			{
				Json::Value item;
				item["_isZipped"] = array[i]._isZipped;
				item["_fsize"] = (Json::Int64)array[i]._fsize;
				item["_atime"] = (Json::Int64)array[i]._atime;
				item["_ctime"] = (Json::Int64)array[i]._ctime;
				item["_mtime"] = (Json::Int64)array[i]._mtime;
				item["_backupPath"] = array[i]._backupPath;
				item["_zipPath"] = array[i]._zipPath;
				item["_reqDownloadPath"] = array[i]._reqDownloadPath;

				root.append(item); // 添加数组元素
			}

			// 3. Json::Value序列化成字符串
			std::string content;
			JsonUtil::Serialize(root, &content);

			// 4. 写文件
			FileUtil fileUtil(_backupAInfoFile);
			fileUtil.SetContent(content);

			return true;
		}

		// 获取当前表中的所有的BackupAInfo 存入array
		bool GetAllBackupAInfo(std::vector<BackupAInfo> *array)
		{
			pthread_rwlock_wrlock(&_rwlock);

			for (auto it = _table.begin(); it != _table.end(); ++it)
				array->push_back(it->second);

			pthread_rwlock_unlock(&_rwlock);
			return true;
		}

		// http://ip:port/path?id=5&num=4#ch /download/a.txt
		bool GetOneByReqDownloadPath(const std::string &reqDownloadPath, BackupAInfo *info)
		{
			pthread_rwlock_wrlock(&_rwlock);
			// reqDownloadPath是key值 直接通过find进行查找
			auto it = _table.find(reqDownloadPath);
			if (it == _table.end())
			{
				pthread_rwlock_unlock(&_rwlock);
				Log::log(LOGERROR, "DataManager::GetOneByReqDownloadPath not find reqDownloadPath-?: %s: %d", strerror(errno), errno);
				return false;
			}
			*info = it->second;
			pthread_rwlock_unlock(&_rwlock);
			return true;
		}

		// /backupDir/a.txt
		bool GetOneByFilePath(const std::string &filePath, BackupAInfo *info)
		{
			pthread_rwlock_wrlock(&_rwlock);
			for (auto it = _table.begin(); it != _table.end(); ++it)
			{
				if (it->second._backupPath == filePath)
				{
					*info = it->second;
					pthread_rwlock_unlock(&_rwlock);
					return true;
				}
			}
			pthread_rwlock_unlock(&_rwlock);
			Log::log(LOGERROR, "DataManager::GetOneByRealPath not find filepath-?: %s: %d", strerror(errno), errno);

			return false;
		}
	};
}
#endif //__MY_DATA__
